package im.composer.audioservers.rmi.comporessed;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.sound.sampled.AudioFormat;

import org.jaudiolibs.audioservers.AudioClient;
import org.jaudiolibs.audioservers.AudioConfiguration;
import org.tritonus.share.sampled.FloatSampleBuffer;

public class GzipRmiAudioAdapter implements AudioClient {

	private final CompressedAudioTerminal terminal;
	private AudioConfiguration context;

	private GzipRmiAudioAdapter(CompressedAudioTerminal terminal) {
		this.terminal = terminal;
	}

	public static GzipRmiAudioAdapter lookup(String host, String path) throws Exception {
		return lookup(host, Registry.REGISTRY_PORT, path);
	}

	public static GzipRmiAudioAdapter lookup(String host, int port, String path) throws Exception {
		Registry myRegistry = LocateRegistry.getRegistry(host, port);
		CompressedAudioTerminal terminal = (CompressedAudioTerminal) myRegistry.lookup(path);
		return new GzipRmiAudioAdapter(terminal);
	}

	@Override
	public void configure(AudioConfiguration context) throws Exception {
		terminal.configure(context);
		this.context = context;
	}

	@Override
	public boolean process(long time, List<FloatBuffer> inputs, List<FloatBuffer> outputs, int nframes) {
		FloatSampleBuffer obuf = null;
		try {
			byte[] input = compressInput(inputs);
			byte[] output = terminal.audioExchange(time, input, nframes);
			obuf = decompressOutput(output);
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (obuf != null) {
			for (int i = 0; i < outputs.size(); i++) {
				outputs.get(i).put(obuf.getChannel(i));
			}
		}
		return true;
	}

	private byte[] compressInput(List<FloatBuffer> inputs) throws IOException {
		int outChannels = inputs.size();
		FloatSampleBuffer ibuf = new FloatSampleBuffer(outChannels, context.getMaxBufferSize(), context.getSampleRate());
		for (int i = 0; i < outChannels; i++) {
			ibuf.setRawChannel(i, inputs.get(i).array());
		}

		AudioFormat format = new AudioFormat(context.getSampleRate(), terminal.getSampleSizeInBits(), outChannels, true, false);
		ByteArrayOutputStream baos = new ByteArrayOutputStream(ibuf.getByteArrayBufferSize(format));
		GZIPOutputStream gos = new GZIPOutputStream(baos);
		byte[] b = ibuf.convertToByteArray(format);
		gos.write(b);
		gos.flush();
		gos.close();
		baos.flush();
		baos.close();
		return baos.toByteArray();
	}

	private FloatSampleBuffer decompressOutput(byte[] output) throws IOException {
		ByteArrayInputStream bais = new ByteArrayInputStream(output);
		int inChannels = context.getInputChannelCount();
		FloatSampleBuffer obuf = new FloatSampleBuffer(inChannels, context.getMaxBufferSize(), context.getSampleRate());
		AudioFormat format = new AudioFormat(context.getSampleRate(), terminal.getSampleSizeInBits(), inChannels, true, false);
		ByteArrayOutputStream baos = new ByteArrayOutputStream(obuf.getByteArrayBufferSize(format));
		GZIPInputStream gis = new GZIPInputStream(bais);
		while (gis.available() > -1) {
			byte[] b = new byte[gis.available()];
			gis.read(b);
			baos.write(b);
		}
		gis.close();
		bais.close();
		return obuf;
	}

	@Override
	public void shutdown() {

	}

}
